package negocio;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Inheritance;
import javax.persistence.InheritanceType;
import javax.persistence.JoinColumn;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;

import exceptions.PlatoException;
import beans.AreaVO;
import beans.PlatoVO;


@Entity
@Table(name="areas")
@Inheritance(strategy=InheritanceType.JOINED)

public class Area implements Serializable 
{
	private static final long serialVersionUID = 1L;
	
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	@Column(name="id_area", nullable=false)
	private int id_area;
	
	@OneToOne(cascade=CascadeType.ALL)
	@JoinColumn(name="id_deposito_area", referencedColumnName="id_deposito")
	private Deposito depositoArea;
	
	@OneToOne(cascade=CascadeType.ALL)
	@JoinColumn(name="id_deposito_local", referencedColumnName="id_deposito")
	private Deposito depositoLocal;
	
	@OneToMany(cascade=CascadeType.ALL, fetch=FetchType.EAGER)
	private Set<Plato> platosPendientes;
	
	@Column(name="nombre", nullable=true)
	private String nombre;
	
	public Area()
	{
		depositoArea = new Deposito();
		platosPendientes = new HashSet<Plato>();
	}
	
	public Area(String nombre, Deposito depositoLocal)
	{
		depositoArea = new Deposito(nombre);
		this.nombre = nombre;
		this.depositoLocal = depositoLocal;
		platosPendientes = new HashSet<Plato>();
	}
	
	public int getId_area() {
		return id_area;
	}
	public void setId_area(int id_area) {
		this.id_area = id_area;
	}
	
	public String getNombre() {
		return nombre;
	}

	public void setNombre(String nombre) {
		this.nombre = nombre;
	}

	public Set<Plato> getPlatosPendientes() {
		return platosPendientes;
	}

	public void setPlatosPendientes(HashSet<Plato> platosPendientes) {
		this.platosPendientes = platosPendientes;
	}

	public Deposito getDepositoArea() {
		return depositoArea;
	}

	public void setDepositoArea(Deposito depositoArea) {
		this.depositoArea = depositoArea;
	}

	public Deposito getDepositoLocal() {
		return depositoLocal;
	}

	public void setDepositoLocal(Deposito depositoLocal) {
		this.depositoLocal = depositoLocal;
	}

	@Override
	public String toString() {
		return "Area [id_area=" + id_area + ", depositoArea=" + depositoArea
				+ ", depositoLocal=" + depositoLocal + ", platosPendientes="
				+ platosPendientes + ", nombre=" + nombre + "]";
	}
	
	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result
				+ ((depositoArea == null) ? 0 : depositoArea.hashCode());
		result = prime * result
				+ ((depositoLocal == null) ? 0 : depositoLocal.hashCode());
		result = prime * result + id_area;
		result = prime * result + ((nombre == null) ? 0 : nombre.hashCode());
		result = prime
				* result
				+ ((platosPendientes == null) ? 0 : platosPendientes.hashCode());
		return result;
	}
	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Area other = (Area) obj;
		if (depositoArea == null) {
			if (other.depositoArea != null)
				return false;
		} else if (!depositoArea.equals(other.depositoArea))
			return false;
		if (depositoLocal == null) {
			if (other.depositoLocal != null)
				return false;
		} else if (!depositoLocal.equals(other.depositoLocal))
			return false;
		if (id_area != other.id_area)
			return false;
		if (nombre == null) {
			if (other.nombre != null)
				return false;
		} else if (!nombre.equals(other.nombre))
			return false;
		if (platosPendientes == null) {
			if (other.platosPendientes != null)
				return false;
		} else if (!platosPendientes.equals(other.platosPendientes))
			return false;
		return true;
	}
	
	public void buildFromVO(AreaVO areaVO)
	{
		
		Deposito dep = new Deposito();
		dep.buildFromVO(areaVO.getDeposito());
		this.setDepositoArea(dep);
		
		Vector<Plato> platos = new Vector<Plato>();
		for (int i = 0 ; i < areaVO.getPlatosPendientes().size() ; i++)
		{
			Plato p = new Plato();
			p.buildFromVO(areaVO.getPlatosPendientes().get(i));
			platos.add(p);
		}
		
		for(Plato p : platos)
			platosPendientes.add(p);
	}

	public AreaVO getVO()
	{
		AreaVO areaVO = new AreaVO();
		areaVO.setDeposito(getDepositoArea().getVO());
		
		ArrayList<PlatoVO> platosVOs = new ArrayList<PlatoVO>();
		for(Plato p : this.getPlatosPendientes())
		{
			PlatoVO platoVO=p.getVO();
			platosVOs.add(platoVO);
		}
		
		areaVO.setPlatosPendientes(platosVOs);
		
		return areaVO;
	}

	public void agregarPlato(Plato plato)
	{
		generarMovimientos(plato);
		platosPendientes.add(plato);
	}
	
	private void generarMovimientos(Plato plato)
	{
		List<Ingrediente> ingredientes = plato.getReceta().getIngredientes();
		for(Ingrediente i : ingredientes){
			Producto p = i.getProducto();
			float stock = depositoArea.calcularStock(p);
			float cantidad = i.getCantidad();
			
			/**TODO: arreglar fechas**/
			
			if(stock < cantidad){
				depositoLocal.realizarMovimiento(p, -(cantidad - stock), 
						nombre, new Date(), new Date(), 0);
				
				depositoArea.realizarMovimiento(p, cantidad - stock, 
						nombre, new Date(), new Date(), 0);
			}
			depositoArea.realizarMovimiento(p, -cantidad, 
					nombre, new Date(), new Date(), 0);
		}
	}
	
	public boolean isPlatoDisponible(Plato plato)
	{
		List<Ingrediente> ingredientes = plato.getReceta().getIngredientes();
		for(Ingrediente i : ingredientes){
			Producto p = i.getProducto();
			if(depositoArea.calcularStock(p) < i.getCantidad()){
				for(Ingrediente j : ingredientes){
					Producto pr = j.getProducto();
					if(depositoLocal.calcularStock(pr) < j.getCantidad())
						return false;
				}
			}
		}
		return true;
	}
	
	public void terminarPlato(Plato plato) throws PlatoException
	{
		if(contienePlato(plato))
			elimiarPlatoPendiente(plato);
		else
			throw new PlatoException("No se encontro el plato en el area.");
	}
	
	private boolean contienePlato(Plato plato)
	{
		for(Plato p : platosPendientes){
			if(p.equals(plato))
				return true;
		}
		return false;
	}
	
	private void elimiarPlatoPendiente(Plato plato)
	{
		for(Plato p : platosPendientes){
			if(p.equals(plato))
				platosPendientes.remove(p);
		}
	}
}
